﻿using System;
using System.Collections.Generic;
using System.Linq;
using GigaSite.Domain.Entities;
using GigaSite.Domain.Enums;
using GigaSite.Domain.Interfaces.Repositories;

namespace GigaSite.Domain.DAL.Repositories
{
    /// <summary>
    /// СУБД реализация репозитория объявлений
    /// </summary>
    public class AdvsRepository: BaseRepository<Adv>, IAdvsRepository
    {
        /// <summary>
        /// Стандартный конструктор
        /// </summary>
        /// <param name="dataContext">Контекст данных</param>
        public AdvsRepository(GigaDataContext dataContext) : base(dataContext)
        {
        }

        /// <summary>
        /// Загружает указанную сущность по ее идентификатору
        /// </summary>
        /// <param name="id">Идентификатор сущности</param>
        /// <returns>Сущность с указанным идентификатором или Null</returns>
        public override Adv Load(long id)
        {
            return Find(a => a.Id == id);
        }

        /// <summary>
        /// Получает список только активных объявлений в категории, отсортированных по дате
        /// </summary>
        /// <param name="category">Категория</param>
        /// <returns>коллекция объявлений в указанной категории</returns>
        public IEnumerable<Adv> GetActiveAdvsInCategory(Category category)
        {
            return
                Search(a => a.CategoryId == category.Id && a.Status == AdvStatuses.Active).OrderByDescending(
                    a => a.Sticked).ThenByDescending(a => a.DateCreated);
        }

        /// <summary>
        /// Добавляет изображение к указанному объявлению
        /// </summary>
        /// <param name="ads">Объявление</param>
        /// <param name="image">Изображение</param>
        public void AddImageToAdvs(Adv ads, Image image)
        {
            // Ищем маппинг для такого изображения
            var mapping = ads.AdvImages.FirstOrDefault(i => i.ImageURI == image.URI);
            if (mapping == null)
            {
                mapping = new AdvImage()
                              {
                                  Adv = ads,
                                  ImageURI = image.URI,
                                  DateCreated = DateTime.Now,
                                  Position = ads.AdvImages.Select(i => i.Position).Max()+1
                              };
                DataContext.AdvImages.InsertOnSubmit(mapping);
            }
        }

        /// <summary>
        /// Получает все объявления в указанной категории а так же во всех ее дочерних подкатегориях
        /// </summary>
        /// <param name="category">Категория</param>
        /// <returns>Коллекция ВСЕХ обяэвлений</returns>
        public IEnumerable<Adv> GetAdsInCategoryAndChildCategories(Category category)
        {
            // TODO: заменить в будущем на хранимую процедуру из БД
            // Получаем список всех категорий
            IList<Category> allCategories = new List<Category>()
                                    {
                                        category
                                    };
            category.GetAllChildCategories(ref allCategories);

            // Выбираем все подкатегории из указанных категорий
            return from cat in allCategories
                   from adv in cat.Advs
                   select adv;
        }

        /// <summary>
        /// Возвращает количество объявлений в указанной категории а так же во всех ее дочерних подкатегориях
        /// </summary>
        /// <param name="category">Категория</param>
        /// <returns>Количество объявлений в категории и ее подкатегориях</returns>
        public int GetAdsCountInCategoryAndChildCategories(Category category)
        {
            // TODO: переделать на получение средством хранимо процедуры
            return GetAdsInCategoryAndChildCategories(category).Count();
        }

        /// <summary>
        /// Удаляет объявления из избранного пользователя
        /// </summary>
        /// <param name="user">Пользователь</param>
        /// <param name="adv">Объявление</param>
        public void RemoveAdsFromUserFavourites(User user, Adv adv)
        {
            // Маппинг
            var mapping = DataContext.AdsToUserMappings.FirstOrDefault(m => m.User == user && m.Adv == adv);
            if (mapping != null)
            {
                DataContext.AdsToUserMappings.DeleteOnSubmit(mapping);
            }
        }

        /// <summary>
        /// Добавляет объявление в избранное к пользователю
        /// </summary>
        /// <param name="user">Пользователь</param>
        /// <param name="adv">Объявление</param>
        public void AddAdsToUserFavourites(User user, Adv adv)
        {
            // Маппинг
            var mapping = DataContext.AdsToUserMappings.FirstOrDefault(m => m.User == user && m.Adv == adv);
            if (mapping == null)
            {
                mapping = new AdsToUserMapping()
                {
                    Adv = adv,
                    User = user,
                    DateAdded = DateTime.Now
                };
                DataContext.AdsToUserMappings.InsertOnSubmit(mapping);
            }
        }

        /// <summary>
        /// Получает коллекцию объявлений которые подал указанный пользователь в категории и во всех ее дочерних подкатегориях
        /// </summary>
        /// <param name="category">Категория</param>
        /// <param name="user">Пользователь</param>
        /// <returns>Коллекция объявлений</returns>
        public IEnumerable<Adv> GetUserAdsInCategoryAndChildCategories(Category category, User user)
        {
            // TODO: заменить в будущем на хранимую процедуру из БД
            // Получаем список всех категорий
            IList<Category> allCategories = new List<Category>()
                                    {
                                        category
                                    };
            category.GetAllChildCategories(ref allCategories);

            // Выбираем все подкатегории из указанных категорий
            return from cat in allCategories
                   from adv in cat.Advs
                   where adv.User == user
                   select adv;
        }

        /// <summary>
        /// Получает количество пользовательских объявлений в категории и во всех ее дочерних подкатегориях
        /// </summary>
        /// <param name="category">Категория</param>
        /// <param name="user">Пользователь</param>
        /// <returns>Количество объявлений которые создал пользовать</returns>
        public int GetUserAdsCountInCategoryAndChildCategories(Category category, User user)
        {
            // TODO: переделать на получение средствами хранимой процедуры
            return GetUserAdsInCategoryAndChildCategories(category, user).Count();
        }

        /// <summary>
        /// Получает коллекцию объявлений из избранного пользователя находящегося в указанных категориях а так же во всех вложенных подкатегориях
        /// </summary>
        /// <param name="category">Категория</param>
        /// <param name="user">Пользователь</param>
        /// <returns>Коллекция объявлений</returns>
        public IEnumerable<Adv> GetFavouriteUserAdsInCategoryAndChildCategories(Category category, User user)
        {
            // TODO: переделать на хранимую процедуру
            // Получаем список всех категорий
            IList<Category> allCategories = new List<Category>()
                                    {
                                        category
                                    };
            category.GetAllChildCategories(ref allCategories);

            // Выбираем объявления
            return from map in user.AdsToUserMappings
                   where allCategories.Any(c => c.Id == map.Adv.CategoryId)
                   select map.Adv;
        }

        /// <summary>
        /// Получает количество объявлений из избранного пользователя, находящегося в указанной категории а так же во всех дочерних подкатегориях
        /// </summary>
        /// <param name="category"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public int GetFavouriteUserAdsCountInCategoryAndChildCategories(Category category, User user)
        {
            // TODO: переделать на хранимую процедуру
            return GetFavouriteUserAdsInCategoryAndChildCategories(category, user).Count();
        }
    }
}